#include <stdint.h>
#include <string.h>
#include <stddef.h>
#include "BK3432_reg.h"
#include "log.h"
#include "adc.h"
#include "gpio.h"
#include "user_config.h"

static hal_adc_cb_t adc_cb = NULL;
static uint16_t referance_voltage = DEFAULT_REFERENCE_VOLTAGE;

#define CALIB_COUNT 6
#define STABL_VALT  75
#define ADC_CH_NUM(pin_num)		(pin_num - GPIOD_0)


void adc_calib_init(uint8_t channel, uint8_t mode)
{
	uint32_t cfg;
	//enable adc clk
	REG_AHB0_ICU_ADCCLKCON &= ~(0x01 << 0);
	//adc div
	REG_AHB0_ICU_ADCCLKCON = (0x5 << 1);
	//set special as peripheral func
	gpio_config(GPIOD_0 + channel, HIRESI, PULL_NONE); 
	//set adc mode/channel/wait clk
	cfg = ((mode << BIT_ADC_MODE) | (channel << BIT_ADC_CHNL) | (0x01 << BIT_ADC_WAIT_CLK_SETTING));
	REG_APB7_ADC_CFG = cfg;
	//set adc sample rate/pre div
	cfg |= ((18 << BIT_ADC_SAMPLE_RATE) | (0 << BIT_ADC_PRE_DIV) | (0x0 << BIT_ADC_DIV1_MODE));
	REG_APB7_ADC_CFG = cfg;
	cfg |= (0x0 << BIT_ADC_FILTER);
	REG_APB7_ADC_CFG = cfg;
	REG_APB7_ADC_CFG |= (0x01 << BIT_ADC_INT_CLEAR);
}


uint16_t adc_calibration_value_get(void)	
{
	uint16_t g_adc_value = 0;
	REG_APB7_ADC_CFG |= SET_ADC_EN + (1 << BIT_ADC_MODE);
	while(REG_APB7_ADC_CFG & (1 << BIT_ADC_BUSY));
	while((REG_APB7_ADC_CFG & (1 << BIT_ADC_FIFO_EMPTY)));
	while(!(REG_APB7_ADC_CFG & (1 << BIT_ADC_FIFO_EMPTY)))
	{
		g_adc_value = ((REG_APB7_ADC_DAT >> 2) & 0xFF);   //8 bit adc samples
	}
	REG_APB7_ADC_CFG &= ~(SET_ADC_EN + (0x03 << BIT_ADC_MODE ));	//Must clear enable bit after read adc value
	return g_adc_value;
}


/*************************ADC calibration process************************
1.Calibrate ADC must supply a stable power to Vbat, Default voltage is 3V.
  STABL_VALT = (Vbat/4 * 100).
2.Use adc_calibration(void) to calculate ADC calibration value -> referance_voltage.
*************************************************************************/
void adc_calibration(void)
{
	uint32_t calib_temp = 0; 
	adc_calib_init(7, 1);
	for(uint8_t i = 0;i < CALIB_COUNT;i++)
	{
		calib_temp += adc_calibration_value_get();	//8 bit adc samples
		Delay_us(1000);
	}
	referance_voltage = (0xff * STABL_VALT)/(calib_temp/CALIB_COUNT);
	LOG_DEBUG("referance_voltage = %d, %d\r\n", referance_voltage, (calib_temp/CALIB_COUNT));
	
	//Need to get couple samples to run out unvalid samples.
	adc_calib_init(ADC_CH_NUM(BATT_ADC_PIN_NUM), 1);
	for(uint8_t i = 0;i < CALIB_COUNT;i++)
	{
		calib_temp += adc_calibration_value_get();
		Delay_us(1000);
	}
}

uint16_t adc_referance_voltage_get(void)
{
	return referance_voltage;
}


/******************************************************/

void adc_cb_register(hal_adc_cb_t cb)
{
	if(cb) {
		adc_cb = cb;
	}
}

void adc_deinit(void)
{
    //gpio_config(0x30 + ADC_CH_NUM(pin_num), FLOAT, PULL_NONE);
    REG_APB7_ADC_CFG &= ~(SET_ADC_EN+(0x03 << BIT_ADC_MODE ));
    REG_AHB0_ICU_ADCCLKCON |= (0x01 << 0);
    REG_AHB0_ICU_INT_ENABLE &= ~(0x01 << 8);
}

//Voltage: 0 ~ 1.05V
//Adc: 0 ~ 1023
void adc_isr(void)
{
	uint32_t adc_value = 0;

    REG_APB7_ADC_CFG |= (0x01 << BIT_ADC_INT_CLEAR);
	
	adc_value = REG_APB7_ADC_DAT;
	REG_APB7_ADC_CFG &= ~(SET_ADC_EN + (0x03 << BIT_ADC_MODE)); //Must clear enable bit after read adc value
    if(adc_cb) {
		adc_cb(adc_value);
	}
}


void adc_trigger(void)
{
	//enable adc clk
	REG_AHB0_ICU_ADCCLKCON &= ~(0x01 << 0);
	//adc div
	REG_AHB0_ICU_ADCCLKCON |= (0x5 << 1);

	//set special as peripheral func
	gpio_config(BATT_ADC_PIN_NUM, FLOAT, PULL_NONE);

	REG_APB7_ADC_CFG = ((0x01 << BIT_ADC_MODE) | (ADC_CH_NUM(BATT_ADC_PIN_NUM) << BIT_ADC_CHNL) | (0x01 << BIT_ADC_WAIT_CLK_SETTING));
	
	REG_APB7_ADC_CFG |= ((18 << BIT_ADC_SAMPLE_RATE) | (0 << BIT_ADC_PRE_DIV) | (0x0 << BIT_ADC_DIV1_MODE));
	
	REG_APB7_ADC_CFG |= (0x0 << BIT_ADC_FILTER);
	
	REG_APB7_ADC_CFG |= (0x01 << BIT_ADC_INT_CLEAR);
	
	REG_AHB0_ICU_INT_ENABLE |= INT_STATUS_ADC_bit;
	
	REG_APB7_ADC_CFG |= SET_ADC_EN;
}

